package com.fansl.allround.auth.provider.sys.mobile;

import com.fansl.allround.common.core.constant.SecurityConstants;
import com.fansl.allround.common.core.constant.enums.OauthTypeEnum;
import com.fansl.allround.common.core.constant.enums.ServiceTypeEnum;
import com.fansl.allround.common.security.provider.AbstractUserDetailsAuthenticationProvider;
import com.fansl.allround.common.security.service.AllroundUserDetailsService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * @author fansl
 * @Description: admin用户手机号登录校验逻辑
 * @date 2019/9/26 13:23
 */
@Slf4j
@AllArgsConstructor
public class SysUserMobileAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    private AllroundUserDetailsService allroundUserDetailsService;
    private RedisTemplate redisTemplate;

    /**
     * 自定义认证校验
     *
     * @param userDetails
     * @param authentication
     * @throws AuthenticationException
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, Authentication authentication) throws AuthenticationException {
        SysUserMobileAuthenticationToken mobileAuthenticationToken = (SysUserMobileAuthenticationToken) authentication;
        String mobile = (String) mobileAuthenticationToken.getPrincipal();
        String smsCode = (String) mobileAuthenticationToken.getCredentials();
        String loginSmsCodeRedisKey =
                String.format(SecurityConstants.LOGIN_SMS_CODE_KEY, ServiceTypeEnum.BACKEND.name(), mobile);
        Object loginSmsCode = redisTemplate.opsForValue().get(loginSmsCodeRedisKey);
        if (null == loginSmsCode || !loginSmsCode.equals(smsCode)) {
            throw new BadCredentialsException("验证码错误");
        }
        //移除验证码
        redisTemplate.delete(loginSmsCodeRedisKey);
        //移除验证码发送次数
        String loginSendSmsCodeRedisKey =
                String.format(SecurityConstants.LOGIN_SEND_SMS_CODE_TIMES_KEY, ServiceTypeEnum.BACKEND.name(), mobile);
        redisTemplate.delete(loginSendSmsCodeRedisKey);
        //移除验证码发送时间间隔
        String loginSendSmsCodeIntervalRedisKey =
                String.format(SecurityConstants.LOGIN_SEND_SMS_CODE_TIMES_INTERVAL_KEY, ServiceTypeEnum.BACKEND.name(), mobile);
        redisTemplate.delete(loginSendSmsCodeIntervalRedisKey);
    }

    /**
     * 查找用户信息
     *
     * @param username
     * @param authentication
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected UserDetails retrieveUser(String username, Authentication authentication) throws AuthenticationException {
        SysUserMobileAuthenticationToken mobileAuthenticationToken = (SysUserMobileAuthenticationToken) authentication;
        String mobile = (String) mobileAuthenticationToken.getPrincipal();
        String smsCode = (String) mobileAuthenticationToken.getCredentials();
        UserDetails user = allroundUserDetailsService.loadUserByOauthType(mobile, OauthTypeEnum.PHONE);
        if (user == null) {
            log.warn("用户未找到，mobile:{},smsCode:{},oauthType:{}", mobile, smsCode, OauthTypeEnum.PHONE);
            throw new UsernameNotFoundException("用户不存在");
        }
        return user;
    }

    /**
     * 生成认证信息
     *
     * @param authentication
     * @param user
     * @return
     */
    @Override
    protected Authentication createSuccessAuthentication(Authentication authentication, UserDetails user) {
        SysUserMobileAuthenticationToken authenticationToken = (SysUserMobileAuthenticationToken) authentication;
        SysUserMobileAuthenticationToken token = new SysUserMobileAuthenticationToken(user, user.getAuthorities());
        token.setDetails(authenticationToken.getDetails());
        return token;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SysUserMobileAuthenticationToken.class.isAssignableFrom(authentication);
    }
}
